#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <ctype.h>
#include "json_parse.h"

#define JSON_ACCESS(code) \
    if (!(code))          \
    return false

static void SkipWhiteSpace(char **buffer);
static bool JsonCheckNumber(char **buffer);
static bool JsonCheckStr(char **buffer);
static bool JsonCheckArray(char **buffer);
static bool JsonCheckValue(char **buffer);
static bool JsonCheckItem(char **buffer);
static bool JsonCheckobject(char **buffer);
static bool FindKeyInTheObject(char **object, char **key);
static bool FindKeyInTheArray(char **array, char **key);

// 指针跳过不可见字符
static void SkipWhiteSpace(char **buffer)
{
    char *str;

    if (buffer == NULL)
    {
        return;
    }

    str = *buffer;

    while (*str && *str <= 32)
    {
        str++;
    }

    *buffer = str;
}

// 检查数字类型的值，合法的话就跳指针
static bool JsonCheckNumber(char **buffer)
{
    char *str;

    JSON_ACCESS(buffer);
    str = *buffer;

    if (*str == '-' || *str == '+')
    {
        str++;
    }

    JSON_ACCESS('0' <= *str && *str <= '9');

    while ('0' <= *str && *str <= '9')
    {
        str++;
    }

    if (*str == '.')
    {
        str++;
        JSON_ACCESS('0' <= *str && *str <= '9');
        while ('0' <= *str && *str <= '9')
        {
            str++;
        }
    }

    if (*str == 'E' || *str == 'e')
    {
        str++;
        if (*str == '-' || *str == '+')
        {
            str++;
        }

        JSON_ACCESS('0' <= *str && *str <= '9');
        while ('0' <= *str && *str <= '9')
        {
            str++;
        }
    }

    *buffer = str;
    return true;
}

// 检查字符串，合法的话就跳指针
static bool JsonCheckStr(char **buffer)
{
    char *str;

    JSON_ACCESS(buffer);
    str = *buffer;

    JSON_ACCESS(*str++ == '\"');
    while (*str && *str != '\"')
    {
        str++;
    }
    JSON_ACCESS(*str++ == '\"');
    *buffer = str;

    return true;
}

// 检查数组，合法的话就跳指针
static bool JsonCheckArray(char **buffer)
{
    char *str;

    JSON_ACCESS(buffer);
    str = *buffer;

    JSON_ACCESS(*str++ == '[');
    SkipWhiteSpace(&str);
    while (JsonCheckValue(&str) || *str == ']')
    {
        SkipWhiteSpace(&str);
        if (*str == ']')
        {
            str++;
            break;
        }
        JSON_ACCESS(*str++ == ',');
        SkipWhiteSpace(&str);
    }
    *buffer = str;

    return true;
}

// 检查所有类型的值，合法的话就跳指针
static bool JsonCheckValue(char **buffer)
{
    char *str;
    bool ret = false;

    JSON_ACCESS(buffer);
    str = *buffer;
    if (strncmp(str, "null", 4) == 0)
    {
        str += 4;
        ret = true;
    }
    else if (strncmp(str, "false", 5) == 0)
    {
        str += 5;
        ret = true;
    }
    else if (strncmp(str, "true", 4) == 0)
    {
        str += 4;
        ret = true;
    }
    else if (*str == '\"' && JsonCheckStr(&str))
    {
        ret = true;
    }
    else if (*str == '-' || *str >= '0' && *str <= '9' && JsonCheckNumber(&str))
    {
        ret = true;
    }
    else if (*str == '[' && JsonCheckArray(&str))
    {
        ret = true;
    }
    else if (*str == '{' && JsonCheckobject(&str))
    {
        ret = true;
    }

    if (ret == true)
    {
        *buffer = str;
    }

    return ret;
}

// 检查项(item),格式为key : value，合法的话就跳指针
static bool JsonCheckItem(char **buffer)
{
    char *str;

    JSON_ACCESS(buffer);
    str = *buffer;
    JSON_ACCESS(JsonCheckStr(&str));
    SkipWhiteSpace(&str);
    JSON_ACCESS(*str++ == ':');
    SkipWhiteSpace(&str);
    JSON_ACCESS(JsonCheckValue(&str));
    *buffer = str;
    return true;
}

// 检查对象(object)，格式为{item, item, item......}，合法的话就跳指针
static bool JsonCheckobject(char **buffer)
{
    char *str;
    JSON_ACCESS(buffer);
    str = *buffer;
    JSON_ACCESS(*str++ == '{');
    SkipWhiteSpace(&str);

    if (*str == '}')
    {
        str++;
        *buffer = str;
        return true;
    }

    while (JsonCheckItem(&str) || *str == '}')
    {
        SkipWhiteSpace(&str);
        if (*str == '}')
        {
            str++;
            break;
        }
        JSON_ACCESS(*str++ == ',');
        SkipWhiteSpace(&str);
    }

    *buffer = str;
    return true;
}

// 获取值(value)的类型
json_type_t JsonGetType(char *str)
{
    if (str == NULL)
    {
        return JSON_INVALID;
    }
    /* null */
    if (strncmp(str, "null", 4) == 0)
    {
        return JSON_NULL;
    }
    /* bool */
    if (strncmp(str, "false", 5) == 0 || strncmp(str, "true", 4) == 0)
    {
        return JSON_BOOL;
    }
    /* string */
    if (*str == '\"' && JsonCheckStr(&str))
    {
        return JSON_STRING;
    }
    /* number */
    if (*str == '-' || *str >= '0' && *str <= '9' && JsonCheckNumber(&str))
    {
        return JSON_NUMBER;
    }
    /* array */
    if (*str == '[' && JsonCheckArray(&str))
    {
        return JSON_ARRAY;
    }
    /* object */
    if (*str == '{' && JsonCheckobject(&str))
    {
        return JSON_OBJECT;
    }
    return JSON_INVALID;
}

// 在对象(object)中查找键(key),找到的话指针就跳到键(key)的位置
// 例：
// 输入*object:"{"one": 1, "two": 2, "three": 3}"     *key:".two"
//                         ↑
// 输出true,并且*object指针跳转到对应位置，*key也跳过".two"
static bool FindKeyInTheObject(char **object, char **key)
{
#define IsAvailableChar(ch) ('0' <= ch && ch <= '9' || 'a' <= ch && ch <= 'z' || 'A' <= ch && ch <= 'Z' || ch == '_')

    char *str;
    char *str_temp;
    char *key_p;
    char *key_p_temp;

    JSON_ACCESS(object && key);
    str = *object;
    key_p = *key;
    JSON_ACCESS(*key_p++ == '.');

    while (*str == ',' || *str == '{')
    {
        str++;
        SkipWhiteSpace(&str);

        str_temp = str;
        key_p_temp = key_p;
        JSON_ACCESS(*str_temp++ == '\"');
#if JSONPATH_CASE_INSENSITIVE
        while (IsAvailableChar(*str_temp) && IsAvailableChar(*key_p_temp) && tolower(*str_temp) == tolower(*key_p_temp))
#else
        while (IsAvailableChar(*str_temp) && IsAvailableChar(*key_p_temp) && *str_temp == *key_p_temp)
#endif
        {
            str_temp++;
            key_p_temp++;
        }

        if (*str_temp == '\"' && IsAvailableChar(*key_p_temp) == false)
        {
            str_temp = str;
            JSON_ACCESS(JsonCheckItem(&str_temp));

            *object = str;
            *key = key_p_temp;

            return true;
        }

        JSON_ACCESS(JsonCheckItem(&str));
        SkipWhiteSpace(&str);
    }

    return false;
}

// 在数组(Array)中查找索引(index),找到的话指针就跳到索引(index)的位置
// 例：
// 输入*array:"["one": 1, "two": 2, "three": 3]"     *key:"[2]"
//                         ↑
// 输出true,并且*array指针跳转到对应位置，*key也跳过"[2]"
static bool FindKeyInTheArray(char **array, char **key)
{
    char *str;
    char *str_temp;
    char *key_p;
    char *key_p_temp;
    int number = 0;

    unsigned char number_c_string[10] = {0};

    JSON_ACCESS(array && key);

    str = *array;
    str_temp = str;
    key_p = *key;
    key_p_temp = key_p;

    JSON_ACCESS(*key_p_temp++ == '[');
    for (int i = 0; '0' <= *key_p_temp && *key_p_temp <= '9'; i++, key_p_temp++)
    {
        JSON_ACCESS(i < (sizeof(number_c_string) - 1));
        number_c_string[i] = *key_p_temp;
    }
    number = atoi((char *)number_c_string);

    JSON_ACCESS(*str_temp++ == '[');
    SkipWhiteSpace(&str_temp);
    for (int i = 0; i < number; i++)
    {
        JSON_ACCESS(JsonCheckValue(&str_temp));
        SkipWhiteSpace(&str_temp);
        JSON_ACCESS(*str_temp++ == ',');
        SkipWhiteSpace(&str_temp);
    }
    SkipWhiteSpace(&str_temp);

    JSON_ACCESS(*key_p_temp++ == ']');

    *array = str_temp;
    *key = key_p_temp;
    return true;
}

// 在Json报文中查找,找到的话指针就跳到对应的位置,不懂jsonpath怎么写可以百度"jsonpath"，或者使用网站练习:https://jsonpath.com/
// 例：
// 输入str:"{"head": 1, "body": {"the left hand", "the right hand"}, "tail": 3}"     json_path:"$.body[1]"
//                                                ↑
// 输出指针指向"the right hand"......
char *JsonPath(char *str, char *format, ...)
{
    char buffer[JSONPATH_BUFFER_SIZE] = {0};
    char *json_path = buffer;
    va_list args;
    va_start(args, format);
    vsnprintf(json_path, JSONPATH_BUFFER_SIZE, format, args); // 将格式化后的字符串存储到buffer中
    va_end(args);

    // 解析jsonpath
    JSON_ACCESS(str && json_path);
    JSON_ACCESS(*json_path++ == '$');
    while (*json_path)
    {
        if (*json_path == '.')
        {
            JSON_ACCESS(FindKeyInTheObject(&str, &json_path));
            JSON_ACCESS(JsonCheckStr(&str));
            SkipWhiteSpace(&str);
            JSON_ACCESS(*str++ == ':');
            SkipWhiteSpace(&str);
        }
        else if (*json_path == '[')
        {
            JSON_ACCESS(FindKeyInTheArray(&str, &json_path));
        }
        else
        {
            return NULL;
        }
    }

    return str;
}

// JsonPath带检查类型功能的版本
char *JsonPathCheck(char *str, json_type_t json_type, char *format, ...)
{
    char buffer[JSONPATH_BUFFER_SIZE] = {0};
    char *json_path = buffer;
    va_list args;
    va_start(args, format);
    vsnprintf(json_path, JSONPATH_BUFFER_SIZE, format, args); // 将格式化后的字符串存储到buffer中
    va_end(args);

    // 解析jsonpath
    str = JsonPath(str, json_path);
    if (JsonGetType(str) != json_type)
    {
        return NULL;
    }

    return str;
}

// 查询数组的项数
int JsonGetItemNumsInArray(char *str)
{
    int item_nums = 0;
    JSON_ACCESS(str);

    JSON_ACCESS(*str++ == '[');

    SkipWhiteSpace(&str);
    while (JsonCheckValue(&str))
    {
        item_nums++;
        SkipWhiteSpace(&str);
        if (*str == ']')
        {
            break;
        }
        JSON_ACCESS(*str++ == ',');
        SkipWhiteSpace(&str);
    }

    return item_nums;
}

// 解析浮点数
double JsonPraseDouble(char *str)
{
    double number = 0;
    int i = 0;
    unsigned char *after_end = NULL;
    unsigned char number_c_string[64];

    JSON_ACCESS(JsonGetType(str) == JSON_NUMBER);
    for (i = 0; (i < (sizeof(number_c_string) - 1)) && *str; i++, str++)
    {
        switch (*str)
        {
        case '0':
        case '1':
        case '2':
        case '3':
        case '4':
        case '5':
        case '6':
        case '7':
        case '8':
        case '9':
        case '+':
        case '-':
        case 'e':
        case 'E':
        case '.':
            number_c_string[i] = *str;
            break;
        default:
            i = sizeof(number_c_string);
            break;
        }
    }

    number_c_string[i] = '\0';
    number = strtod((char *)number_c_string, (char **)&after_end);
    JSON_ACCESS(number_c_string != after_end);
    return number;
}

// 解析整型
int JsonPraseInt(char *str)
{
    JSON_ACCESS(JsonGetType(str) == JSON_NUMBER);
    return (int)JsonPraseDouble(str);
}

// 解析布尔
bool JsonPraseBool(char *str)
{
    JSON_ACCESS(JsonGetType(str) == JSON_BOOL);
    return strncmp(str, "true", 4) == 0;
}

// 解析字符串
int JsonPraseStr(char *str, char *buf, unsigned int max_len)
{
    int len;
    JSON_ACCESS(str && buf);
    JSON_ACCESS(JsonGetType(str) == JSON_STRING);
    JSON_ACCESS(*str++ == '\"');
    for (len = 0; len < max_len && *str && *str != '\"'; len++)
    {
        buf[len] = *str++;
    }

    return len;
}

// 实例
#ifdef example
void main(void)
{
    char *json =
        "{\
        \"firstName\": \"John\",\
        \"lastName\": \"doe\",\
        \"age\": 26,\
        \"address\": {\
            \"streetAddress\": \"naist street\",\
            \"city\": \"Nara\",\
            \"postalCode\": \"630-0192\"\
        },\
        \"phoneNumbers\": [\
            {\
                \"type\": \"iPhone\",\
                \"number\": \"0123-4567-8888\"\
            },\
            {\
                \"type\": \"home\",\
                \"number\": \"0123-4567-8910\"\
            },\
            true,\
        ],\
        \"deposit\": 1e8,\
        \"SportsCar\": [\
            \"Bugatti Veyron\",\
            \"Lamborghini Aventador\",\
            \"Ferrari 488 GTB\",\
            \"McLaren P1\",\
            \"Porsche 911 GT2 RS\",\
            \"Aston Martin Vulcan\",\
            \"Koenigsegg Jesko\",\
            \"Pagani Huayra\",\
            \"Lotus Evija\",\
            \"Rimac C_Two\",\
        ],\
    }";

    char buffer[50] = {0};
    int len = 0;
    double double_number;
    int number;
    bool b;
    char *array;



#if example == 1
    char *p;

    // 解析字符串
    if ((p = JsonPathCheck(json, JSON_STRING, "$.phoneNumbers[1].number")) != NULL)
    {
        len = JsonPraseStr(p, buffer, sizeof(buffer) - 1);
    }

    // 解析int
    if ((p = JsonPathCheck(json, JSON_NUMBER, "$.age")) != NULL)
    {
        number = JsonPraseInt(p);
    }

    // 解析double
    if ((p = JsonPathCheck(json, JSON_NUMBER, "$.deposit")) != NULL)
    {
        double_number = JsonPraseDouble(p);
    }

    // 解析bool
    if ((p = JsonPathCheck(json, JSON_BOOL, "$.phoneNumbers[2]")) != NULL)
    {
        b = JsonPraseBool(p);
    }

    printf("buffer: %s, len : %d\r\n", buffer, len);
    printf("number: %d\r\n", number);
    printf("double_number: %f\r\n", double_number);
    printf("b: %d\r\n", b);
#elif example == 2

    // 如果不在乎待解析json报文格式是否正确，可以使用简便写法：
    // 解析字符串
    len = JsonPraseStr(JsonPath(json, "$.phoneNumbers[1].number"), buffer, sizeof(buffer) - 1);
    // 解析int
    number = JsonPraseInt(JsonPath(json, "$.age"));
    // 解析double
    number = JsonPraseDouble(JsonPath(json, "$.deposit"));
    // 解析bool
    array = JsonPraseBool(JsonPath(json, "$.phoneNumbers[2]"));

    printf("buffer: %s, len : %d\r\n", buffer, len);
    printf("number: %d\r\n", number);
    printf("double_number: %f\r\n", double_number);
    printf("b: %d\r\n", b);

#elif example == 3
    // 如果数组(Array)中的项数是不确定的，可以先用JsonGetItemNumsInArray()获取项数：
    int item_nums = JsonGetItemNumsInArray(JsonPath(json, "$.SportsCar"));
    printf("%d\r\n", item_nums);
    char *p;
    for (int i = 0; i < item_nums; i++)
    {
        if ((p = JsonPathCheck(json, JSON_STRING, "$.SportsCar[%d]", i)) != NULL)
        {
            JsonPraseStr(p, buffer, sizeof(buffer) - 1);
            printf("SportsCar[%d]: %s\r\n", i, buffer);
            memset(buffer, 0, sizeof(buffer));
        }
    }

#endif
}
#endif
